1 /*
2 * Copyright (C) 2010 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15 package com.google.common.collect;
16
17 import static com.google.common.base.Preconditions.checkNotNull;
18
19 import com.google.common.annotations.Beta;
20 import com.google.common.annotations.GwtCompatible;
21 import com.google.common.base.Function;
22
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.List;
26 import java.util.RandomAccess;
27
28 import javax.annotation.Nullable;
29
30 /**
31 * Static methods pertaining to sorted {@link List} instances.
32 *
33 * In this documentation, the terms <i>greatest</i>, <i>greater</i>, <i>least</i>, and
34 * <i>lesser</i> are considered to refer to the comparator on the elements, and the terms
35 * <i>first</i> and <i>last</i> are considered to refer to the elements' ordering in a
36 * list.
37 *
38 * @author Louis Wasserman
39 */
40 @GwtCompatible
41 @Beta final class SortedLists {
42 private SortedLists() {}
43
44 /**
45 * A specification for which index to return if the list contains at least one element that
46 * compares as equal to the key.
47 */
48 public enum KeyPresentBehavior {
49 /**
50 * Return the index of any list element that compares as equal to the key. No guarantees are
51 * made as to which index is returned, if more than one element compares as equal to the key.
52 */
53 ANY_PRESENT {
54 @Override
55 <E> int resultIndex(
56 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
57 return foundIndex;
58 }
59 },
60 /**
61 * Return the index of the last list element that compares as equal to the key.
62 */
63 LAST_PRESENT {
64 @Override
65 <E> int resultIndex(
66 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
67 // Of course, we have to use binary search to find the precise
68 // breakpoint...
69 int lower = foundIndex;
70 int upper = list.size() - 1;
71 // Everything between lower and upper inclusive compares at >= 0.
72 while (lower < upper) {
73 int middle = (lower + upper + 1) >>> 1;
74 int c = comparator.compare(list.get(middle), key);
75 if (c > 0) {
76 upper = middle - 1;
77 } else { // c == 0
78 lower = middle;
79 }
80 }
81 return lower;
82 }
83 },
84 /**
85 * Return the index of the first list element that compares as equal to the key.
86 */
87 FIRST_PRESENT {
88 @Override
89 <E> int resultIndex(
90 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
91 // Of course, we have to use binary search to find the precise
92 // breakpoint...
93 int lower = 0;
94 int upper = foundIndex;
95 // Of course, we have to use binary search to find the precise breakpoint...
96 // Everything between lower and upper inclusive compares at <= 0.
97 while (lower < upper) {
98 int middle = (lower + upper) >>> 1;
99 int c = comparator.compare(list.get(middle), key);
100 if (c < 0) {
101 lower = middle + 1;
102 } else { // c == 0
103 upper = middle;
104 }
105 }
106 return lower;
107 }
108 },
109 /**
110 * Return the index of the first list element that compares as greater than the key, or {@code
111 * list.size()} if there is no such element.
112 */
113 FIRST_AFTER {
114 @Override
115 public <E> int resultIndex(
116 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
117 return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1;
118 }
119 },
120 /**
121 * Return the index of the last list element that compares as less than the key, or {@code -1}
122 * if there is no such element.
123 */
124 LAST_BEFORE {
125 @Override
126 public <E> int resultIndex(
127 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
128 return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1;
129 }
130 };
131 abstract <E> int resultIndex(
132 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex);
133 }
134
135 /**
136 * A specification for which index to return if the list contains no elements that compare as
137 * equal to the key.
138 */
139 public enum KeyAbsentBehavior {
140 /**
141 * Return the index of the next lower element in the list, or {@code -1} if there is no such
142 * element.
143 */
144 NEXT_LOWER {
145 @Override
146 int resultIndex(int higherIndex) {
147 return higherIndex - 1;
148 }
149 },
150 /**
151 * Return the index of the next higher element in the list, or {@code list.size()} if there is
152 * no such element.
153 */
154 NEXT_HIGHER {
155 @Override
156 public int resultIndex(int higherIndex) {
157 return higherIndex;
158 }
159 },
160 /**
161 * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at
162 * which the key would be inserted into the list: the index of the next higher element in the
163 * list, or {@code list.size()} if there is no such element.
164 *
165 * <p>Note that the return value will be {@code >= 0} if and only if there is an element of the
166 * list that compares as equal to the key.
167 *
168 * <p>This is equivalent to the behavior of
169 * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't present, since
170 * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}.
171 */
172 INVERTED_INSERTION_INDEX {
173 @Override
174 public int resultIndex(int higherIndex) {
175 return ~higherIndex;
176 }
177 };
178
179 abstract int resultIndex(int higherIndex);
180 }
181
182 /**
183 * Searches the specified naturally ordered list for the specified object using the binary search
184 * algorithm.
185 *
186 * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
187 * KeyAbsentBehavior)} using {@link Ordering#natural}.
188 */
189 public static <E extends Comparable> int binarySearch(List<? extends E> list, E e,
190 KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) {
191 checkNotNull(e);
192 return binarySearch(
193 list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior);
194 }
195
196 /**
197 * Binary searches the list for the specified key, using the specified key function.
198 *
199 * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
200 * KeyAbsentBehavior)} using {@link Ordering#natural}.
201 */
202 public static <E, K extends Comparable> int binarySearch(List<E> list,
203 Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior,
204 KeyAbsentBehavior absentBehavior) {
205 return binarySearch(
206 list,
207 keyFunction,
208 key,
209 Ordering.natural(),
210 presentBehavior,
211 absentBehavior);
212 }
213
214 /**
215 * Binary searches the list for the specified key, using the specified key function.
216 *
217 * <p>Equivalent to
218 * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} using
219 * {@link Lists#transform(List, Function) Lists.transform(list, keyFunction)}.
220 */
221 public static <E, K> int binarySearch(
222 List<E> list,
223 Function<? super E, K> keyFunction,
224 @Nullable K key,
225 Comparator<? super K> keyComparator,
226 KeyPresentBehavior presentBehavior,
227 KeyAbsentBehavior absentBehavior) {
228 return binarySearch(
229 Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior);
230 }
231
232 /**
233 * Searches the specified list for the specified object using the binary search algorithm. The
234 * list must be sorted into ascending order according to the specified comparator (as by the
235 * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior
236 * to making this call. If it is not sorted, the results are undefined.
237 *
238 * <p>If there are elements in the list which compare as equal to the key, the choice of
239 * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to
240 * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned.
241 *
242 * <p>This method runs in log(n) time on random-access lists, which offer near-constant-time
243 * access to each list element.
244 *
245 * @param list the list to be searched.
246 * @param key the value to be searched for.
247 * @param comparator the comparator by which the list is ordered.
248 * @param presentBehavior the specification for what to do if at least one element of the list
249 * compares as equal to the key.
250 * @param absentBehavior the specification for what to do if no elements of the list compare as
251 * equal to the key.
252 * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list;
253 * otherwise the index determined by the {@code KeyAbsentBehavior}.
254 */
255 public static <E> int binarySearch(List<? extends E> list, @Nullable E key,
256 Comparator<? super E> comparator, KeyPresentBehavior presentBehavior,
257 KeyAbsentBehavior absentBehavior) {
258 checkNotNull(comparator);
259 checkNotNull(list);
260 checkNotNull(presentBehavior);
261 checkNotNull(absentBehavior);
262 if (!(list instanceof RandomAccess)) {
263 list = Lists.newArrayList(list);
264 }
265 // TODO(user): benchmark when it's best to do a linear search
266
267 int lower = 0;
268 int upper = list.size() - 1;
269
270 while (lower <= upper) {
271 int middle = (lower + upper) >>> 1;
272 int c = comparator.compare(key, list.get(middle));
273 if (c < 0) {
274 upper = middle - 1;
275 } else if (c > 0) {
276 lower = middle + 1;
277 } else {
278 return lower + presentBehavior.resultIndex(
279 comparator, key, list.subList(lower, upper + 1), middle - lower);
280 }
281 }
282 return absentBehavior.resultIndex(lower);
283 }
284 }